package com.seuic.hnapps.device_implement;

import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;

import com.seuic.android.ICCardTransFinishListener;
import com.seuic.android.ReadCardListener;

import com.seuic.extra.emv.EmvTransStartListener;
import com.seuic.hnapps.constants.EmvConstants;
import com.seuic.hnapps.posdsdk.device_interface.EmvDevice;
import com.seuic.hnapps.util.HexUtil;

import java.util.List;

public class EmvDeviceImp extends BaseImplement implements EmvDevice {

    private static EmvDeviceImp self;

    static EmvDeviceImp getInstance() {
        if ( self == null ) {
            synchronized ( EmvDeviceImp.class ) {
                if ( self == null ) {
                    self = new EmvDeviceImp();
                }
            }
        }
        return self;
    }

    @Override
    public int downloadAids(List<String> aids) {
        if ( aids == null ) {
            Log.e( LOG_TAG, "aids == null" );
            setLastError( "Input Param Error" );
            return -1;
        }
        if ( icCardReader == null ) {
            Log.e( LOG_TAG, "icCardReader == null" );
            setLastError( "Emv Service Error" );
            return -1;
        }
        try {
            int result = icCardReader.clearAIDs();
            if ( result < 0 ) {
                Log.e( LOG_TAG, "icCardReader.clearAIDs Failed: " + result );
                setLastError( "Emv Service Execute Error: " + result );
                return -2;
            }
            for ( String aid : aids ) {
                result = icCardReader.downloadAID( HexUtil.hexStringToByteArray( aid ) );
                if ( result < 0 ) {
                    Log.e( LOG_TAG, "icCardReader.downloadAID Failed: " + result );
                    setLastError( "Emv Service Execute Error: " + result );
                    return -2;
                }
            }
        } catch (RemoteException e) {
            Log.e( LOG_TAG, "RemoteException: " + e );
            setLastError( "Emv Service Error" );
            return -3;
        }

        return 0;
    }

    @Override
    public int downloadCapks(List<String> capks) {
        if ( capks == null ) {
            Log.e( LOG_TAG, "capks == null" );
            setLastError( "Input Param Error" );
            return -1;
        }
        if ( icCardReader == null ) {
            Log.e( LOG_TAG, "icCardReader == null" );
            setLastError( "Emv Service Error" );
            return -1;
        }
        try {
            int result = icCardReader.clearPubkeys();
            if ( result < 0 ) {
                Log.e( LOG_TAG, "icCardReader.clearPubkeys Failed: " + result );
                setLastError( "Emv Service Execute Error: " + result );
                return -2;
            }
            for ( String capk : capks ) {
                result = icCardReader.downloadPubkey( HexUtil.hexStringToByteArray( capk ) );
                if ( result < 0 ) {
                    Log.e( LOG_TAG, "icCardReader.downloadPubkey Failed: " + result );
                    setLastError( "Emv Service Execute Error: " + result );
                    return -2;
                }
            }
        } catch (RemoteException e) {
            Log.e( LOG_TAG, "RemoteException: " + e );
            setLastError( "Emv Service Error" );
            return -3;
        }

        return 0;
    }

    @Override
    public int readerConfig(boolean magcardOn, boolean iccardOn, boolean rfcardOn) {
        if ( icCardReader == null ) {
            Log.e( LOG_TAG, "icCardReader == null" );
            setLastError( "Emv Service Error" );
            return -1;
        }
        int result;
        try {
            result = icCardReader.readerConfig( magcardOn ? ( byte ) 1 : ( byte ) 0,
                                       iccardOn ? ( byte ) 1 : ( byte ) 0,
                                       rfcardOn ? ( byte ) 1 : ( byte ) 0 );
        } catch (RemoteException e) {
            setLastError( "Emv Service Error" );
            return -3;
        }
        return result;
    }

    private EmvDeviceReadCardCallback readCardCallback = null;

    @Override
    public void readCard(EmvDeviceReadCardCallback readCardCallback, int overtime) {
        if ( readCardCallback == null ) {
            setLastError( "Input Param Error" );
            return;
        }
        this.readCardCallback = readCardCallback;
        if ( icCardReader == null ) {
            readCardCallbackOnFailed( "Service Error" );
            return;
        }
        try {
            int result = icCardReader.readCard( new PosdReadCardCallback(), overtime );
            if ( 0 != result ) {
                readCardCallbackOnFailed( "Service exe error, readCard: " + result );
            }
        } catch (RemoteException e) {
            readCardCallbackOnFailed( "RemoteException: " + e );
        }
    }

    private void readCardCallbackOnFailed(final String error ) {
        if ( readCardCallback == null ) {
            return;
        }
        handler.post(new Runnable() {
            @Override
            public void run() {
                readCardCallback.OnFailed( error );
            }
        });
    }

    private class PosdReadCardCallback extends ReadCardListener.Stub {

        @Override
        public void onIccIn() throws RemoteException {
            if ( readCardCallback == null ) {
                return;
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    readCardCallback.onICCardIn();
                }
            });
        }

        @Override
        public void onIcclIn() throws RemoteException {
            if ( readCardCallback == null ) {
                return;
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    readCardCallback.onRFCardIn();
                }
            });
        }

        @Override
        public void onMagCardIn(final byte[] pan, final byte[] track1, final byte[] track2,
                                final byte[] track3, final byte[] expDate) throws RemoteException {
            if ( readCardCallback == null ) {
                return;
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    Bundle bundle = new Bundle();
                    if ( pan != null ) {
                        bundle.putString( EmvConstants.PAN, HexUtil.byteToHexString( pan ) );
                    }
                    if ( track1 != null ) {
                        bundle.putString( EmvConstants.TRACK1, HexUtil.byteToHexString( track1 ) );
                    }
                    if ( track2 != null ) {
                        bundle.putString( EmvConstants.TRACK2, HexUtil.byteToHexString( track2 ) );
                    }
                    if ( track3 != null ) {
                        bundle.putString( EmvConstants.TRACK3, HexUtil.byteToHexString( track3 ) );
                    }
                    if ( expDate != null ) {
                        bundle.putString( EmvConstants.CARD_EXP_DATE, HexUtil.byteToHexString( expDate ) );
                    }
                    readCardCallback.onMagcardIn( bundle );
                }
            });
        }

        @Override
        public void onReadCardFail(int error_code) throws RemoteException {
            if ( error_code == -6 ) {
                readCardCallbackOnFailed( "User canceled" );
            } else {
                readCardCallbackOnFailed("ReadCard Error: " + error_code);
            }
        }

        @Override
        public void onMifareCardIn(byte[] uid, byte[] atqa) throws RemoteException {
            if ( readCardCallback == null ) {
                return;
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    readCardCallback.onRFCardIn();
                }
            });
        }
    }

    @Override
    public int powerOn() {
        if ( icCardReader == null ) {
            setLastError( "Service Error" );
            return -1;
        }
        try {
            return icCardReader.powerOn();
        } catch (RemoteException e) {
            setLastError( "RemoteException:" + e );
            return -2;
        }
    }

    @Override
    public String exchangeApdu(String cmd) {
        if ( icCardReader == null ) {
            setLastError( "Service Error" );
            return null;
        }
        if ( cmd == null ) {
            setLastError( "Input Param Error" );
            return null;
        }
        try {
            byte[] recv_byte = new byte[256];
            int[] recv_len = new int[1];
            int result = icCardReader.apdu( HexUtil.hexStringToByteArray( cmd ), recv_byte, recv_len );
            if ( 0 != result ) {
                setLastError( "Service Execute Error: ICCard APDU: " + result );
                return null;
            }
            return HexUtil.byteToHexString( recv_byte, recv_len[0] );
        } catch (RemoteException e) {
            setLastError( "RemoteException:" + e );
            return null;
        }
    }

    @Override
    public int powerOff() {
        if ( icCardReader == null ) {
            setLastError( "Service Error" );
            return -1;
        }
        try {
            return icCardReader.powerOff();
        } catch (RemoteException e) {
            setLastError( "RemoteException:" + e );
            return -2;
        }
    }

    private TransStartCallback transStartCallback = null;

    @Override
    public void transStart(TransStartCallback transStartCallback, Bundle params) {
        if ( transStartCallback == null ) {
            setLastError( "Input Param Error" );
            return;
        }
        this.transStartCallback = transStartCallback;
        if ( params == null ) {
            transStartCallbackOnFailed( "Input Param Error" );
            return;
        }
        if ( emvFuncs == null ) {
            transStartCallbackOnFailed( "Emv Service Error" );
            return;
        }
        try {
            int result = emvFuncs.transStart( new PosdEmvTransStartListener(), params );
            if ( 0 != result ) {
                transStartCallbackOnFailed( "Service Execute Error: " + result );
            }
        } catch (RemoteException e) {
            Log.e( LOG_TAG, "RemoteException: " + e );
            transStartCallbackOnFailed( "Service Error: RemoteException" );
        }
    }

    @Override
    public void cancel() {
        if ( icCardReader == null ) {
            transStartCallbackOnFailed( "ICCard Service Error" );
            return;
        }
        try {
            icCardReader.stopRequest();
        } catch (RemoteException e) {
            Log.e( LOG_TAG, "RemoteException: " + e );
        }
    }

    @Override
    public void selectApp(int index) {
        try {
            emvFuncs.selectApp( index );
        } catch (RemoteException e) {
            Log.e( LOG_TAG, "RemoteException: " + e );
            transStartCallbackOnFailed( "Service Error: RemoteException" );
        }
    }

    @Override
    public void confirmCert(String confirmResult) {
        try {
            emvFuncs.confirmCert( confirmResult );
        } catch (RemoteException e) {
            Log.e( LOG_TAG, "RemoteException: " + e );
            transStartCallbackOnFailed( "Service Error: RemoteException" );
        }
    }

    @Override
    public void confirmCardInfo(String confirmResult) {
        try {
            emvFuncs.confirmCardInfo( confirmResult );
        } catch (RemoteException e) {
            Log.e( LOG_TAG, "RemoteException: " + e );
            transStartCallbackOnFailed( "Service Error: RemoteException" );
        }
    }

    @Override
    public void finishEnterPin(boolean pin_success, String pin) {
        try {
            emvFuncs.finishPin( pin_success, pin );
        } catch (RemoteException e) {
            Log.e( LOG_TAG, "RemoteException: " + e );
            transStartCallbackOnFailed( "Service Error: RemoteException" );
        }
    }

    private void transStartCallbackOnFailed(final String error ) {
        if ( transStartCallback == null ) {
            return;
        }
        handler.post(new Runnable() {
            @Override
            public void run() {
                transStartCallback.onFailed( error );
            }
        });
    }

    private class PosdEmvTransStartListener extends EmvTransStartListener.Stub {

        @Override
        public void onCardInsert(final String type) throws RemoteException {
            if ( transStartCallback == null ) {
                return;
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    transStartCallback.onCardInsert( type );
                }
            });
        }

        @Override
        public void onOnlinePin() throws RemoteException {
            if ( transStartCallback == null ) {
                return;
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    transStartCallback.onEnterPin( false, 0 );

                }
            });
        }

        @Override
        public void onSelect(final List<String> appList) throws RemoteException {
            if ( transStartCallback == null ) {
                return;
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    transStartCallback.onAppSelect( appList );
                }
            });
        }

        @Override
        public void onConfirmCert(final String certType, final String certNum) throws RemoteException {
            if ( transStartCallback == null ) {
                return;
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    transStartCallback.onConfirmCert( certType, certNum );
                }
            });
        }

        @Override
        public void onConfirmCardInfo(final Bundle cardInfoToConfirm) throws RemoteException {
            Log.i( LOG_TAG, "Confirm Card Info: " + cardInfoToConfirm );
            if ( transStartCallback == null ) {
                return;
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    transStartCallback.onConfirmCardInfo( cardInfoToConfirm );
                }
            });
        }

        @Override
        public void onOfflinePin(final int leftTimes) throws RemoteException {
            if ( transStartCallback == null ) {
                return;
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    transStartCallback.onEnterPin( false, leftTimes );
                }
            });
        }

        @Override
        public void OnSuccess(final Bundle data) throws RemoteException {
            if ( transStartCallback == null ) {
                return;
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    transStartCallback.onSuccess( data );
                }
            });
        }

        @Override
        public void OnFallback() throws RemoteException {
            if ( transStartCallback == null ) {
                return;
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    transStartCallback.onFallback();
                }
            });
        }

        @Override
        public void OnFail(final int returnCode) throws RemoteException {
            if ( transStartCallback == null ) {
                return;
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                    switch ( -returnCode ) {
                        case 0x02:
                            transStartCallback.onFailed( "Trade Time Over" );
                            break;
                        case 0x06:
                            transStartCallback.onFailed( "Trade Canceled" );
                            break;
                        case 0x0a:
                            transStartCallback.onUseICCard();
                            break;
                        case 0x0b:
                            transStartCallback.onFallback();
                            break;
                        case 0x16:
                            transStartCallback.onFailed( "Card Collision" );
                            break;
                        case 0x17:
                            transStartCallback.onFailed( "Card Locked" );
                            break;
                        case 0x18:
                            transStartCallback.onFailed( "App Locked" );
                            break;
                        case 0x1b:
                            transStartCallback.onFailed( "ECash Not Support" );
                            break;
                        case 0x1c:
                            transStartCallback.onFailed( "Amount Over ECash Limit" );
                            break;
                        case 0x1d:
                            transStartCallback.onFailed( "Offline Authentication Failed" );
                            break;
                        case 0x1e:
                            transStartCallback.onFailed( "Amount Over Offline Limit" );
                            break;
                        case 0x1f:
                            transStartCallback.onFailed( "qPBOC: Use IC Card" );
                            break;
                        case 0x20:
                            transStartCallback.onFailed( "No Supported App" );
                            break;
                        case 0x21:
                            transStartCallback.onFailed( "Pan & Track2 Mismatched" );
                            break;
                        case 0x22:
                            transStartCallback.onFailed( "Cardholder Verify Failed" );
                            break;
                        case 0x23:
                            transStartCallback.onFailed( "Not Support Online" );
                            break;
                        default:
                            transStartCallback.onFailed( "EMV Error: " + returnCode );
                            break;
                    }
                }
            });
        }
    }

    private  InputOnlineResultCallback inputOnlineResultCallback = null;

    @Override
    public void inputOnlineResult(InputOnlineResultCallback inputOnlineResultCallback, Bundle params) {
        if ( inputOnlineResultCallback == null ) {
            Log.e( LOG_TAG, "inputOnlineResultCallback == null" );
            setLastError( "Input Param Error" );
            return;
        }
        this.inputOnlineResultCallback = inputOnlineResultCallback;
        if ( icCardReader == null ) {
            Log.e( LOG_TAG, "icCardReader == null" );
            inputOnlineResultCallbackOnFailed( "Emv Service Error" );
            return;
        }
        if ( params == null ) {
            inputOnlineResultCallbackOnFailed( "Input Param Error" );
            return;
        }
        String rsp_code = params.getString(EmvConstants.RESPONSE_CODE);
        String field55 = params.getString(EmvConstants.ISSUER_FIELD_55);
        if ( rsp_code == null ) {
            inputOnlineResultCallbackOnFailed( "Input Param Error" );
            return;
        }
        byte[] issuer_tlv;
        if ( field55 != null ) {
            issuer_tlv = HexUtil.hexStringToByteArray(field55 + "8A02" + rsp_code);
        } else {
            issuer_tlv = HexUtil.hexStringToByteArray( "8A02" + rsp_code );
        }
        try {
            int result = icCardReader.transFinish( new PosdTransFinishListener(), issuer_tlv, issuer_tlv.length, 120000 );
            if ( 0 != result ) {
                Log.e( LOG_TAG, "transFinish Failed: " + result );
                inputOnlineResultCallbackOnFailed( "Service Execute Error: " + result );
            }
        } catch (RemoteException e) {
            Log.e( LOG_TAG, "RemoteException: " + e );
            inputOnlineResultCallbackOnFailed( "Service Error" );
        }
    }

    private void inputOnlineResultCallbackOnFailed(final String error ) {
        if ( inputOnlineResultCallback == null ) {
            return;
        }
        handler.post(new Runnable() {
            @Override
            public void run() {
                inputOnlineResultCallback.onFailed( error );
            }
        });
    }

    private void inputOnlineResultCallbackOnSuccess(final Bundle data ) {
        if ( inputOnlineResultCallback == null ) {
            return;
        }
        handler.post(new Runnable() {
            @Override
            public void run() {
                inputOnlineResultCallback.onSuccess( data );
            }
        });
    }

    private class PosdTransFinishListener extends ICCardTransFinishListener.Stub {

        @Override
        public void OnTC(byte[] outdata, int outdata_len, byte[] script_result, int script_result_len) throws RemoteException {
            Bundle bundle = new Bundle();
            bundle.putString( EmvConstants.AC_TYPE, EmvConstants.AC_TYPE_TC );
            if ( outdata != null && outdata_len >0 ) {
                bundle.putString(EmvConstants.FEILD_55, HexUtil.byteToHexString(outdata, outdata_len));
            }
            if ( script_result != null && script_result_len > 0 ) {
                bundle.putString(EmvConstants.SCRIPT_RESULT, HexUtil.byteToHexString(script_result, script_result_len));
            }
            inputOnlineResultCallbackOnSuccess( bundle );
        }

        @Override
        public void OnAAC(byte[] outdata, int outdata_len, byte[] script_result, int script_result_len) throws RemoteException {
            Bundle bundle = new Bundle();
            bundle.putString( EmvConstants.AC_TYPE, EmvConstants.AC_TYPE_AAC );
            if ( outdata != null && outdata_len >0 ) {
                bundle.putString(EmvConstants.FEILD_55, HexUtil.byteToHexString(outdata, outdata_len));
            }
            if ( script_result != null && script_result_len > 0 ) {
                bundle.putString(EmvConstants.SCRIPT_RESULT, HexUtil.byteToHexString(script_result, script_result_len));
            }
            inputOnlineResultCallbackOnSuccess( bundle );
        }

        @Override
        public void OnFail(int returnCode) throws RemoteException {
            inputOnlineResultCallbackOnFailed("Service Execute Error: " + returnCode);
        }
    }
}
